package com.quectel.core.module.device.service.impl.mongo;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import com.quectel.constant.core.ResourceGroupCodeConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.core.constants.MongoCollectionConstants;
import com.quectel.core.module.MongoAuthComponent;
import com.quectel.core.module.device.dto.DeviceCommandTemplateDto;
import com.quectel.core.module.device.dto.DeviceCurrentAlarmDto;
import com.quectel.core.module.device.dto.mongo.MongoDeviceAlarmLogDto;
import com.quectel.core.module.device.dto.mongo.MongoDeviceCommandLogDto;
import com.quectel.core.module.device.dto.mongo.MongoDeviceDataLogDto;
import com.quectel.core.module.device.entity.DeviceCommandTemplateEntity;
import com.quectel.core.module.device.entity.DeviceCurrentAlarmEntity;
import com.quectel.core.module.device.entity.DeviceEntity;
import com.quectel.core.module.device.entity.mongo.MongoDeviceAlarmLogModel;
import com.quectel.core.module.device.entity.mongo.MongoDeviceCommandLogModel;
import com.quectel.core.module.device.entity.mongo.MongoDeviceDataLogModel;
import com.quectel.core.module.device.service.DeviceService;
import com.quectel.core.module.device.service.mongo.MongoDeviceService;
import com.quectel.core.module.resource.service.ResourceGroupService;
import com.quectel.util.common.CopyUtils;
import com.quectel.util.common.DateUtils;
import com.quectel.util.common.Request;
import com.quectel.util.kit.Snowflake;
import com.quectel.util.mongo.MongodbUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@DubboService
public class MongoDeviceServiceImpl implements MongoDeviceService {
    @Autowired
    private MongoAuthComponent mongoAuthComponent;
    @Autowired
    private ResourceGroupService resourceGroupService;
    @Autowired
    private DeviceService deviceService;


    private Criteria sqlWhereForDeviceCommand(Map<String, Object> params) {
        Request request = Request.configParams(params);


        Criteria authByAllQuery = mongoAuthComponent.buildAuthByAllQuery(params, "deviceEntity.tenantId",
                "deviceEntity.deviceCategoryId", "deviceEntity.deviceTypeId",
                "deviceEntity._id", "deviceEntity.villageId", "deviceEntity.buildingId");

        Criteria filterResourceGroupCode = new Criteria();
        if (StringUtils.isNotBlank(request.getString(ResourceGroupCodeConstants.AUTH_RESOURCE_GROUP_CODE_FLAG))) {
            filterResourceGroupCode.andOperator(mongoAuthComponent.buildFilterByResourceGroupQuery(params, "deviceEntity.deviceCategoryId", "deviceEntity.deviceTypeId"));
        }

        Criteria otherFieldQuery = new Criteria();

        Date startTime = request.getDate("startTime", DateUtils.FormatType.COMMON.getType());
        Date endTime = request.getDate("endTime", DateUtils.FormatType.COMMON.getType());

        if (startTime != null && endTime != null) {
            otherFieldQuery.and("sendTime").gte(startTime).lte(endTime);
        }
        Long deviceId = request.getLong("deviceId");
        if (deviceId != null) {
            otherFieldQuery.and("deviceEntity._id").is(deviceId);
        }
        return new Criteria().andOperator(authByAllQuery, filterResourceGroupCode, otherFieldQuery);
    }

    @Override
    public int queryDeviceCommandLogTotal(Map<String, Object> params) {

        return (int) MongodbUtils.count(new Query(sqlWhereForDeviceCommand(params)), MongoCollectionConstants.DEVICE_COMMAND_LOG);
    }

    @Override
    public List<MongoDeviceCommandLogDto> queryDeviceCommandLog(Map<String, Object> params) {
        Request request = Request.configParams(params);
        return
                MongodbUtils.page(
                        new Query(sqlWhereForDeviceCommand(params)),
                        request.getPageNo() - 1,
                        request.getPageSize(),
                        Sort.by(Sort.Direction.DESC, SystemConstants.MONGO_ENTITY_ID_FLAG),
                        MongoDeviceCommandLogModel.class,
                        MongoCollectionConstants.DEVICE_COMMAND_LOG
                ).stream().map(model -> {
                    MongoDeviceCommandLogDto mongoDeviceAlarmLogDto = CopyUtils.copyObj(model, MongoDeviceCommandLogDto.class);
                    if (model.getDeviceEntity() != null && model.getDeviceEntity().getId() != null) {
                        mongoDeviceAlarmLogDto.setDeviceDto(deviceService.selectCacheById(model.getDeviceEntity().getId()));
                    }
                    mongoDeviceAlarmLogDto.setDeviceCommandTemplateDto(CopyUtils.copyObj(model.getDeviceCommandTemplateEntity(), DeviceCommandTemplateDto.class));
                    return mongoDeviceAlarmLogDto;
                }).collect(Collectors.toList());
    }

    @Override
    public Long saveCommandData(MongoDeviceCommandLogDto mongoDeviceCommandLogDto) {
        MongoDeviceCommandLogModel mongoDeviceCommandLogModel = CopyUtils.copyObj(mongoDeviceCommandLogDto, MongoDeviceCommandLogModel.class);
        //dto转为entity防止存入多余字段
        mongoDeviceCommandLogModel.setDeviceEntity(CopyUtils.copyObj(mongoDeviceCommandLogDto.getDeviceDto(), DeviceEntity.class));
        mongoDeviceCommandLogModel.setDeviceCommandTemplateEntity(CopyUtils.copyObj(mongoDeviceCommandLogDto.getDeviceCommandTemplateDto(), DeviceCommandTemplateEntity.class));

        mongoDeviceCommandLogModel.setId(Snowflake.nextId());
        MongodbUtils.add(mongoDeviceCommandLogModel, MongoCollectionConstants.DEVICE_COMMAND_LOG);
        return mongoDeviceCommandLogModel.getId();
    }


    private Criteria sqlWhereForDeviceAlarmLog(Map<String, Object> params) {
        Request request = Request.configParams(params);
        Long deviceId = request.getLong("deviceId");
        Long deviceTypeId = request.getLong("deviceTypeId");
        Long deviceCategoryId = request.getLong("deviceCategoryId");
        Long villageId = request.getLong("villageId");


        Criteria authByAllQuery = mongoAuthComponent.buildAuthByAllQuery(params, "deviceEntity.tenantId",
                "deviceEntity.deviceCategoryId", "deviceEntity.deviceTypeId",
                "deviceEntity._id", "deviceEntity.villageId", "deviceEntity.buildingId");

        Criteria filterResourceGroupCode = new Criteria();
        if (StringUtils.isNotBlank(request.getString(ResourceGroupCodeConstants.AUTH_RESOURCE_GROUP_CODE_FLAG))) {
            filterResourceGroupCode.andOperator(mongoAuthComponent.buildFilterByResourceGroupQuery(params, "deviceEntity.deviceCategoryId", "deviceEntity.deviceTypeId"));
        }


        Criteria otherFieldQuery = new Criteria();
        if (request.isNotBlank("startTime") && request.isNotBlank("endTime")) {

            Date startTime = request.getDate("startTime", DateUtils.FormatType.COMMON.getType());
            Date endTime = request.getDate("endTime", DateUtils.FormatType.COMMON.getType());

            otherFieldQuery.and("startTime").gte(startTime).lte(endTime);
        }
        if (deviceId != null) {
            otherFieldQuery.and("deviceEntity._id").is(deviceId);
        }
        if (villageId != null) {
            otherFieldQuery.and("deviceEntity.villageId").is(villageId);
        }
        if (deviceTypeId != null) {
            otherFieldQuery.and("deviceEntity.deviceTypeId").is(deviceId);
        }
        if (deviceCategoryId != null) {
            otherFieldQuery.and("deviceEntity.deviceCategoryId").is(deviceCategoryId);
        }
        return new Criteria().andOperator(authByAllQuery, filterResourceGroupCode, otherFieldQuery);
    }

    @Override
    public Map<String, Integer> groupDeviceAlarmLogCount(Map<String, Object> searchMap, String field) {
        return MongodbUtils.groupCountBy(sqlWhereForDeviceAlarmLog(searchMap), field, MongoCollectionConstants.DEVICE_ALARM_LOG);
    }

    @Override
    public int queryDeviceAlarmLogTotal(Map<String, Object> params) {

        return (int) MongodbUtils.count(new Query(sqlWhereForDeviceAlarmLog(params)), MongoCollectionConstants.DEVICE_ALARM_LOG);
    }

    @Override
    public List<MongoDeviceAlarmLogDto> queryDeviceAlarmLog(Map<String, Object> params) {

        Request request = Request.configParams(params);

        return MongodbUtils.page(
                new Query(sqlWhereForDeviceAlarmLog(params)),
                request.getPageNo() - 1,
                request.getPageSize(),
                Sort.by(Sort.Direction.DESC, SystemConstants.MONGO_ENTITY_ID_FLAG),
                MongoDeviceAlarmLogModel.class,
                MongoCollectionConstants.DEVICE_ALARM_LOG
        ).stream().map(model -> {
            MongoDeviceAlarmLogDto mongoDeviceAlarmLogDto = CopyUtils.copyObj(model, MongoDeviceAlarmLogDto.class);
            if (model.getDeviceEntity() != null && model.getDeviceEntity().getId() != null) {
                mongoDeviceAlarmLogDto.setDeviceDto(deviceService.selectCacheById(model.getDeviceEntity().getId()));
            }
            mongoDeviceAlarmLogDto.setDeviceCurrentAlarmDto(CopyUtils.copyObj(model.getDeviceCurrentAlarmEntity(), DeviceCurrentAlarmDto.class));
            return mongoDeviceAlarmLogDto;
        }).collect(Collectors.toList());
    }

    private Criteria sqlWhereForReportData(Map<String, Object> params) {
        Request request = Request.configParams(params);

        Date startTime = request.getDate("startTime", DateUtils.FormatType.COMMON.getType());
        Date endTime = request.getDate("endTime", DateUtils.FormatType.COMMON.getType());
        Long deviceId = request.getLong("deviceId");

        Criteria authByAllQuery = mongoAuthComponent.buildAuthByAllQuery(params, "deviceEntity.tenantId",
                "deviceEntity.deviceCategoryId", "deviceEntity.deviceTypeId",
                "deviceEntity._id", "deviceEntity.villageId", "deviceEntity.buildingId");

        Criteria filterResourceGroupCode = new Criteria();
        if (StringUtils.isNotBlank(request.getString(ResourceGroupCodeConstants.AUTH_RESOURCE_GROUP_CODE_FLAG))) {
            filterResourceGroupCode.andOperator(mongoAuthComponent.buildFilterByResourceGroupQuery(params, "deviceEntity.deviceCategoryId", "deviceEntity.deviceTypeId"));
        }

        Criteria otherFieldQuery = new Criteria();
        if (startTime != null && endTime != null) {
            otherFieldQuery.and("createTime").gte(startTime).lte(endTime);
        }
        if (deviceId != null) {
            otherFieldQuery.and("deviceEntity._id").is(deviceId);
        }
        return new Criteria().andOperator(authByAllQuery, filterResourceGroupCode, otherFieldQuery);
    }

    @Override
    public int queryReportDataTotal(Map<String, Object> params) {

        return (int) MongodbUtils.count(new Query(sqlWhereForReportData(params)), MongoCollectionConstants.DEVICE_DATA_LOG);

    }

    @Override
    public List<MongoDeviceDataLogDto> queryReportData(Map<String, Object> params) {
        Request request = Request.configParams(params);
        return MongodbUtils.page(
                new Query(sqlWhereForReportData(params)),
                request.getPageNo() - 1,
                request.getPageSize(),
                Sort.by(Sort.Direction.DESC, SystemConstants.MONGO_ENTITY_ID_FLAG),
                MongoDeviceDataLogModel.class,
                MongoCollectionConstants.DEVICE_DATA_LOG
        ).stream().map(model -> {
            MongoDeviceDataLogDto mongoDeviceDataLogDto = CopyUtils.copyObj(model, MongoDeviceDataLogDto.class);
            if (model.getDeviceEntity() != null && model.getDeviceEntity().getId() != null) {
                mongoDeviceDataLogDto.setDeviceDto(deviceService.selectCacheById(model.getDeviceEntity().getId()));
            }
            return mongoDeviceDataLogDto;

        }).collect(Collectors.toList());
    }

    @Override
    public void saveReportData(MongoDeviceDataLogDto mongoDeviceDataLogDto) {

        MongoDeviceDataLogModel mongoDeviceDataLogModel = CopyUtils.copyObj(mongoDeviceDataLogDto, MongoDeviceDataLogModel.class);
        //dto转为entity防止存入多余字段
        mongoDeviceDataLogModel.setDeviceEntity(CopyUtils.copyObj(mongoDeviceDataLogDto.getDeviceDto(), DeviceEntity.class));

        mongoDeviceDataLogModel.setId(Snowflake.nextId());

        MongodbUtils.add(mongoDeviceDataLogModel, MongoCollectionConstants.DEVICE_DATA_LOG);
    }

    @Override
    public void saveDeviceAlarmLog(MongoDeviceAlarmLogDto mongoDeviceAlarmLogDto) {

        MongoDeviceAlarmLogModel mongoDeviceAlarmLogModel = CopyUtils.copyObj(mongoDeviceAlarmLogDto, MongoDeviceAlarmLogModel.class);

        //dto转为entity防止存入多余字段
        mongoDeviceAlarmLogModel.setDeviceEntity(CopyUtils.copyObj(mongoDeviceAlarmLogDto.getDeviceDto(), DeviceEntity.class));
        mongoDeviceAlarmLogModel.setDeviceCurrentAlarmEntity(CopyUtils.copyObj(mongoDeviceAlarmLogDto.getDeviceCurrentAlarmDto(), DeviceCurrentAlarmEntity.class));

        mongoDeviceAlarmLogModel.setId(Snowflake.nextId());

        MongodbUtils.add(mongoDeviceAlarmLogModel, MongoCollectionConstants.DEVICE_ALARM_LOG);
    }

    @Override
    public void recoveryAlarmLog(DeviceCurrentAlarmDto deviceCurrentAlarmDto, Byte handMethod) {
        MongodbUtils.update(
                new Query(
                        Criteria.where("deviceEntity._id").is(deviceCurrentAlarmDto.getDeviceId())
                                .and("deviceCurrentAlarmEntity.ruleId").is(deviceCurrentAlarmDto.getRuleId())
                                .and("recoveryState").is(SystemConstants.ABNORMAL)
                                //不能无限期的去更新，长期不修复的不处理
                                .and("startTime").gte(DateUtil.offsetMonth(new Date(), -1).toJdkDate())
                ),
                new Update()
                        .set(LambdaUtil.getFieldName(MongoDeviceAlarmLogDto::getFinishTime), new Date())
                        .set(LambdaUtil.getFieldName(MongoDeviceAlarmLogDto::getRecoveryState), SystemConstants.NORMAL)
                        .set(LambdaUtil.getFieldName(MongoDeviceAlarmLogDto::getHandMethod), handMethod),
                MongoCollectionConstants.DEVICE_ALARM_LOG

        );

    }


}
